#!/usr/bin/env python3
"""
WMS Sample 客户端打包脚本
基于PyInstaller打包完整指南
"""

import os
import sys
import shutil
import subprocess
import json
from pathlib import Path
from datetime import datetime

def create_client_deployment_package():
    """创建客户端部署包"""
    
    app_name = "WMS_Client"
    main_file = "client_gui.py"
    
    # 生成时间戳
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    package_name = f"{app_name}_v2.1.0_Production_{timestamp}"
    deployment_dir = Path(package_name)
    
    print("=" * 50)
    print("WMS Sample 客户端打包")
    print("基于PyInstaller打包完整指南")
    print("=" * 50)
    print(f"创建部署包: {package_name}")
    
    try:
        # 1. 准备客户端配置
        prepare_client_config()
        
        # 2. 清理构建文件
        cleanup_build_files()
        
        # 3. 创建部署目录
        deployment_dir.mkdir(exist_ok=True)
        
        # 4. 创建客户端spec文件
        spec_file = create_client_spec(main_file)
        
        # 5. 打包主程序
        if not build_executable(main_file, spec_file, deployment_dir):
            return False
        
        # 6. 复制客户端文件
        copy_client_files(deployment_dir)
        
        # 7. 创建启动脚本（避免emoji乱码）
        create_client_startup_scripts(deployment_dir, app_name)
        
        # 8. 创建说明文档
        create_client_documentation(deployment_dir, app_name)
        
        # 9. 验证部署包
        if verify_client_deployment(deployment_dir):
            print(f"✅ 客户端部署包创建成功: {package_name}")
            print_client_usage_info(package_name)
            return True
        else:
            print(f"❌ 客户端部署包验证失败")
            return False
            
    except Exception as e:
        print(f"❌ 客户端打包失败: {e}")
        return False

def prepare_client_config():
    """准备客户端配置"""
    print("\n=== 准备客户端配置 ===")
    
    # 创建默认客户端配置
    client_config = {
        "api_host": "127.0.0.1",
        "api_port": 58501,
        "connection_timeout": 5,
        "auto_refresh_interval": 30,
        "window_title": "WMS Sample 客户端",
        "theme": "default"
    }
    
    config_file = Path("client_config.json")
    if not config_file.exists():
        with open(config_file, 'w', encoding='utf-8') as f:
            json.dump(client_config, f, indent=2, ensure_ascii=False)
        print("✓ 创建默认客户端配置")
    else:
        print("✓ 客户端配置已存在")

def cleanup_build_files():
    """清理构建文件"""
    print("\n=== 清理构建文件 ===")
    
    dirs_to_clean = ['build', 'dist']
    
    for dir_name in dirs_to_clean:
        if Path(dir_name).exists():
            shutil.rmtree(dir_name)
            print(f"✓ 清理目录: {dir_name}")
    
    # 清理旧的客户端spec文件
    for spec_file in Path('.').glob('client*.spec'):
        if spec_file.exists():
            spec_file.unlink()
            print(f"✓ 清理文件: {spec_file}")

def create_client_spec(main_file):
    """创建客户端spec文件"""
    print("\n=== 创建客户端spec文件 ===")
    
    spec_content = f'''# -*- mode: python ; coding: utf-8 -*-

# 解决递归深度问题
import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5)

a = Analysis(
    ['{main_file}'],
    pathex=[],
    binaries=[],
    datas=[
        ('client_config.json', '.'),  # 客户端配置文件
        ('src', 'src'),               # 源代码目录（客户端需要路径管理器）
    ],
    hiddenimports=[
        # GUI依赖
        'tkinter',
        'tkinter.ttk',
        'tkinter.messagebox',
        '_tkinter',
        
        # 网络依赖
        'requests',
        'urllib3',
        'http.client',
        'email',
        'json',
        'ssl',
        
        # 系统依赖
        'json',
        'threading',
        'pathlib',
        
        # 项目依赖
        'src.core.path_manager',
        
        # 服务器端依赖（确保客户端能够使用）
        'fastapi',
        'uvicorn',
        'sqlalchemy',
        'pydantic',
        'starlette',
        'anyio',
        'idna',
        'sniffio',
        'httptools',
        'watchfiles',
        'websockets',
        'email_validator',
    ],
    hookspath=['./'],  # 使用当前目录中的hook文件
    hooksconfig={{}},
    runtime_hooks=[],
    excludes=[
        # 排除大型库
        'matplotlib', 'numpy', 'pandas', 'scipy', 'PIL',
        'PyQt5', 'PyQt6', 'PySide2', 'PySide6', 'wx',
        'IPython', 'jupyter', 'notebook', 'sphinx', 'docutils',
        'pytest', 'unittest', 'test'
    ],
    noarchive=False,
    optimize=0,
)

pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='WMS_Client',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=False,  # GUI程序不显示控制台
    disable_windowed_traceback=False,
)

coll = COLLECT(
    exe,
    a.binaries,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='WMS_Client',
)
'''
    
    spec_file = Path("client.spec")
    with open(spec_file, 'w', encoding='utf-8') as f:
        f.write(spec_content)
    
    print(f"✓ 创建spec文件: {spec_file}")
    return spec_file

def build_executable(main_file, spec_file, output_dir):
    """构建可执行文件"""
    print("\n=== 打包客户端程序 ===")
    
    try:
        # 使用干净环境的PyInstaller
        pyinstaller_path = "wms_clean_env/Scripts/pyinstaller.exe"
        if not Path(pyinstaller_path).exists():
            print(f"❌ 找不到干净环境的PyInstaller: {pyinstaller_path}")
            print("请先激活干净环境: wms_clean_env\\Scripts\\activate")
            return False
        
        # 确保包含所有必要的依赖
        print("确保包含所有必要的依赖...")
        
        # 使用虚拟环境中的Python路径
        venv_python = "wms_clean_env\\Scripts\\python.exe"
        venv_prefix = "wms_clean_env"
        
        # 使用spec文件直接打包
        cmd = [
            pyinstaller_path,
            '--clean',
            '--distpath', str(output_dir),
            str(spec_file)
        ]
        
        print("正在打包客户端程序...")
        print(f"执行命令: {' '.join(cmd)}")
        print("=" * 60)
        
        # 显示完整打包过程
        result = subprocess.run(cmd)
        
        if result.returncode == 0:
            print("=" * 60)
            print("✅ 客户端程序打包成功")
            
            # 移动程序文件到部署目录根目录
            client_source = output_dir / "WMS_Client"
            if client_source.exists():
                # 移动WMS_Client目录中的所有内容到部署目录根目录
                for item in client_source.iterdir():
                    target_path = output_dir / item.name
                    if item.is_dir():
                        if target_path.exists():
                            shutil.rmtree(target_path)
                        shutil.move(str(item), str(target_path))
                    else:
                        if target_path.exists():
                            target_path.unlink()
                        shutil.move(str(item), str(target_path))
                
                # 删除空的WMS_Client目录
                if client_source.exists():
                    shutil.rmtree(client_source)
                print("✅ 程序文件已正确部署")
            else:
                print("⚠️  程序文件位置可能不同，请检查")
            
            return True
        else:
            print(f"❌ 客户端程序打包失败: 返回码 {result.returncode}")
            return False
            
    except Exception as e:
        print(f"❌ 客户端程序打包失败: {e}")
        return False

def copy_client_files(deployment_dir):
    """复制客户端文件"""
    print("\n=== 复制客户端文件 ===")
    
    # 客户端需要的文件
    files_to_copy = [
        'client_config.json',
    ]
    
    # 客户端需要的目录（如果存在）
    dirs_to_copy = [
        'docs',  # 文档目录
    ]
    
    for file_name in files_to_copy:
        if Path(file_name).exists():
            shutil.copy2(file_name, deployment_dir)
            print(f"✓ 复制文件: {file_name}")
    
    for dir_name in dirs_to_copy:
        if Path(dir_name).exists():
            shutil.copytree(dir_name, deployment_dir / dir_name, dirs_exist_ok=True)
            print(f"✓ 复制目录: {dir_name}")

def create_client_startup_scripts(deployment_dir, app_name):
    """创建客户端启动脚本（避免emoji乱码）"""
    print("\n=== 创建启动脚本 ===")
    
    # Windows启动脚本 - 使用GBK编码，避免emoji
    start_script = deployment_dir / "启动客户端.bat"
    with open(start_script, 'w', encoding='gbk') as f:
        f.write(f"""@echo off
title {app_name}
color 0A
echo.
echo ========================================
echo    WMS Sample 客户端 v2.1.0
echo ========================================
echo.
echo 正在启动客户端程序...
echo.
echo 使用说明：
echo 1. 确保服务器已启动
echo 2. 在连接面板中配置服务器地址
echo 3. 点击"连接"按钮连接服务器
echo.
echo ========================================
echo.

WMS_Client.exe

echo.
echo 客户端已关闭
pause
""")
    print("✅ 主启动脚本")
    
    # 系统检查脚本 - 使用GBK编码
    check_script = deployment_dir / "检查客户端.bat"
    with open(check_script, 'w', encoding='gbk') as f:
        f.write(f"""@echo off
title 客户端检查
color 0B
echo.
echo ========================================
echo    WMS Sample 客户端检查
echo ========================================
echo.

set /a count=0

if exist WMS_Client.exe (
    echo [OK] 客户端程序存在
    set /a count+=1
) else (
    echo [ERROR] 客户端程序不存在
)

if exist _internal\\ (
    echo [OK] 依赖目录存在
    set /a count+=1
) else (
    echo [ERROR] 依赖目录不存在
)

if exist client_config.json (
    echo [OK] 配置文件存在
    set /a count+=1
) else (
    echo [ERROR] 配置文件不存在
)

echo.
if %count%==3 (
    echo [SUCCESS] 客户端检查通过！可以正常使用
    echo 请运行"启动客户端.bat"启动程序
) else (
    echo [WARNING] 客户端检查发现问题，请检查缺失的文件
)

echo.
pause
""")
    print("✅ 系统检查脚本")

def create_client_documentation(deployment_dir, app_name):
    """创建客户端说明文档"""
    print("\n=== 创建使用说明 ===")
    
    # 简单的使用说明
    readme_file = deployment_dir / "使用说明.txt"
    with open(readme_file, 'w', encoding='utf-8') as f:
        f.write(f"""
WMS Sample 客户端 v2.1.0 使用说明
{'=' * 50}

这是什么？
WMS Sample 系统的客户端程序，用于连接到WMS服务器进行料盘和样品管理。

如何使用？
1. 双击运行"检查客户端.bat"确认程序正常
2. 双击运行"启动客户端.bat"启动客户端
3. 在连接面板中配置服务器地址和端口
4. 点击"连接"按钮连接到WMS服务器
5. 开始使用料盘管理和样品管理功能

文件说明：
├── WMS_Client.exe          客户端主程序
├── _internal/              程序依赖文件（不要删除）
├── client_config.json      客户端配置文件
├── 启动客户端.bat          启动脚本（双击运行）
├── 检查客户端.bat          系统检查脚本
└── 使用说明.txt            本文件

连接配置：
- 默认服务器地址：127.0.0.1
- 默认端口：58501
- 可在程序中修改连接参数

主要功能：
1. 料盘管理：创建、修改、删除料盘，查看料位状态
2. 样品管理：放置、移除样品，查看样品位置
3. 查询功能：搜索样品，查看统计信息
4. 实时同步：与服务器实时同步数据

故障排除：
1. 无法启动？
   - 运行"检查客户端.bat"检查文件完整性
   - 确保所有文件在同一目录下

2. 无法连接服务器？
   - 检查服务器是否已启动
   - 确认服务器地址和端口正确
   - 检查网络连接

3. 功能异常？
   - 点击"刷新"按钮重新加载数据
   - 重新连接服务器
   - 重启客户端程序

技术支持：
如遇问题，请联系开发团队

开始使用吧！
双击"启动客户端.bat"即可开始！
""")
    print("✅ 用户使用说明")

def verify_client_deployment(deployment_dir):
    """验证客户端部署包"""
    print("\n=== 验证部署包 ===")
    
    checks = []
    
    # 检查主程序
    exe_file = deployment_dir / "WMS_Client.exe"
    checks.append(("客户端程序", exe_file.exists()))
    
    # 检查依赖目录
    internal_dir = deployment_dir / "_internal"
    checks.append(("依赖目录", internal_dir.exists()))
    
    # 检查配置文件
    config_file = deployment_dir / "client_config.json"
    checks.append(("配置文件", config_file.exists()))
    
    # 检查启动脚本
    start_script = deployment_dir / "启动客户端.bat"
    checks.append(("启动脚本", start_script.exists()))
    
    # 检查说明文档
    readme_file = deployment_dir / "使用说明.txt"
    checks.append(("使用说明", readme_file.exists()))
    
    # 输出检查结果
    print("客户端部署包验证结果：")
    for name, result in checks:
        status = "✅" if result else "❌"
        print(f"{status} {name}")
    
    all_passed = all(result for _, result in checks)
    
    if all_passed:
        # 计算部署包大小
        total_size = sum(f.stat().st_size for f in deployment_dir.rglob('*') if f.is_file())
        size_mb = total_size / (1024 * 1024)
        print(f"\n📊 部署包大小: {size_mb:.1f} MB")
    
    return all_passed

def print_client_usage_info(package_name):
    """打印客户端使用信息"""
    print("\n" + "=" * 50)
    print("🎉 WMS Sample 客户端部署包创建成功！")
    print("=" * 50)
    print(f"📦 部署包名称: {package_name}")
    print(f"📁 部署包位置: {Path(package_name).absolute()}")
    
    # 列出主要文件
    main_files = [
        "WMS_Client.exe",
        "启动客户端.bat", 
        "检查客户端.bat",
        "使用说明.txt",
        "client_config.json"
    ]
    
    print("\n📋 部署包内容:")
    for file_name in main_files:
        print(f"  ✅ {file_name}")
    
    print(f"\n🚀 使用方法:")
    print(f"1. 将 {package_name} 文件夹复制到用户计算机")
    print(f"2. 双击运行 '检查客户端.bat' 验证系统")
    print(f"3. 双击运行 '启动客户端.bat' 启动客户端")
    print(f"4. 配置服务器连接参数并连接")
    
    print(f"\n💡 注意事项:")
    print(f"- 确保WMS服务器已启动")
    print(f"- 客户端需要网络访问服务器")
    print(f"- 默认连接地址：http://127.0.0.1:58501")
    
    print(f"\n🎉 客户端部署包创建完成！")

if __name__ == "__main__":
    create_client_deployment_package() 